/* ----------------------------------------------------------------------------

	pax.widget.hint.js Copyright (C) 2009 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */
/*
	Script: pax.widget.hint
		This is a hint widget, that shows hints (or "tool tips") near elements
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>
		
*/

var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.hint = pax.widget.hint || {};

/*	Method: pax.widget.hint.init
	Shows hints at the given targets
	
	Parameters:		
		target - an element to render the hint into
		args - arguments for the grid widget
		* target - Element to show the hint near, you MUST supply this.
		* events - List of which event(s) to show the hint on, default: ['mouseover']
		* hideEvents - List of which event(s) to hide the hint on, default: ['mouseout']
		* message -  Hint message
		* messageAttribute - If we want to use a specific attribute for the message text, eg: alt or title
		* hintClass - The CSS class name, default is 'hintMessage'
		* width - Width of the hint in pixels, use the hintClass to set the default with and height of the hints
		* height - Height of the hint, leave this empty, for height to automatically fit the content
		* x - How many pixels to offset the hint by, horizontally from the element
		* y - How many pixels to offset the hint by, vertically from the elements right side
        * offsetElement - Element to optionally offset the hint from, if you don't want the trigger element
		* opacity - Opacity in percent, default is 100
		* clickToHide - Do we wait for mouse out of the element, or a click on the hint? Default is false
		* dontHideOnHover - Do we keep showing the hint until you mouse out?
		* showDelay - Delay in milliseconds to wait till we show the hint on hover, default is null
		* hideDelay - Delay in milliseconds to wait till we hide the hint, default is 20, should always be at least 20
		* id - Optional ID of hint box, default is auto generated
		* showFunc - Optional function to show the hint with, see <pax.fx> for functions.
		* hideFunc - Optional function to hide the hint with, see <pax.fx> for functions.

	Returns:
		Element of the hint

	Example:
		(start code)
			<a href='#' id='widget.hint.init.example1'>Hover me for hint</a>
			[:.
				pax.widget.hint.init( {
					'widget.hint.init.example1': 'testing 123'
				} );
			:]
		(end)
		This example shows a simple hint
		
	Example:
		(start code)
			<a href='#' id='widget.hint.init.example2'>Hover me for hint that fades in and out, with a delay</a>
			[:.
				pax.widget.hint.init( {
					'widget.hint.init.example2': {
						showFunc: pax.fx.fadeIn,
						hideFunc: pax.fx.fadeOut,
						opacity: 0,
						message: 'testing 123<br>new line',
						showDelay: 300,
						hideDelay: 2000
					}
				} );
			:]
		(end)
		This example shows a hint that fades in and out. Note the use of "opacity: 0", as that is required for the fadein.
		
	Example:
		(start code)
			<a href='#' id='widget.hint.init.example3'>Hover me for hint that fades in and out, with a delay</a>
			<img src='http://paxjs.com/resource/logo.png' id='widget.hint.init.example3.img'/>
			[:.
				pax.widget.hint.init( {
					'widget.hint.init.example2': {
						message: 'testing 123<br>new line',
						showFunc: null,
						showDelay: null
					}, 
					'widget.hint.init.example3.img': 'This is a message from an image hover'
				}, {
					showFunc: pax.fx.fadeIn,
					hideFunc: pax.fx.fadeOut,
					opacity: 0,
					showDelay: 300,
					hideDelay: 2000
				} );
			:]
		(end)
		This example shows two hints that fade in and out. We are using the defaultModel feature, to allow the hints to share the configuration. 
		Note that you can override the default configuration parameters, by specifying them within each hint, as done on the first hint.

	Example:
		(start code)
			<a href='#' id='widget.hint.init.example4'>Hover me for hint initialised from the title attribute</a>
			[:.
				pax.widget.hint.init( ['widget.hint.init.example4'] } );
			:]
		(end)
		This example shows a hint that uses the title attribute for the message, this allows fallback if JS is not available.
		Note that you can set which attribute to use, by specifying a messageAttribute in the configuration
		
*/
pax.widget.hint.init = function( args, defaultModel ) {
	//	Iterate on arguments, and initialise hints
	var isList = (pax.util.getType( args ) == 'array');
	for( var target in args ) {
		var argument = args[target];
		var model = {};
		target = ( isList )? argument: target;
		
		if( typeof( argument ) == 'string' ) {
			//	If we are using a list, get the message from the title or alt attribute
			model = pax.widget.hint.model( ( (defaultModel)? defaultModel: {} ) );
			//	Default to using the title attribute
			if( isList && model['messageAttribute'] == null ) {
				model['messageAttribute'] = 'title';
			} else {
				if( model['messageAttribute'] )model['messageAttribute'] = model['messageAttribute'].toLowerCase();
			}
			var message = ( model['messageAttribute'] )? pax.$(target).getAttribute( model['messageAttribute'] ): argument;
			
			model['message'] = message;
		} else {
			//	Use the argument as the model
			if( defaultModel ) {
				model = pax.widget.hint.model( defaultModel );
				model = pax.defaultArgs( model, argument );
			} else {
				model = pax.widget.hint.model( argument );
			}
			
			if( model['messageAttribute'] ) {
				model['messageAttribute'] = model['messageAttribute'].toLowerCase();
				model['message'] = pax.$(target).getAttribute( model['messageAttribute'] );
			}
			
		}
		//	Find target, can be in args, model or defaul to the key
		model['target'] = args.target || model.target || target;
		
		pax.widget.hint.initHint( model );
	}
};


/*	Private Method: pax.widget.hint.initHint
	This function initialises the show events for a hint
*/
pax.widget.hint.initHint = function( args ) {
	var hintBox = null;
	var hintId = ( args.id )? args.id: pax.getNextId();
	var target = pax.$( args.target );
	if( args['messageAttribute'] != null ) {
		//	Note: IE need the attribute name in lowercase; all other browsers seem to work with this too.
		target.setAttribute( args['messageAttribute'].toLowerCase(), '' );
	}

	if( pax.util.getType( args.events ) == 'string' )args.events = [args.events]; 
	if( pax.util.getType( args.hideEvents ) == 'string' )args.hideEvents = [args.hideEvents]; 
	
	var hideHint = function( hideFunc ) {
		pax.box.hide( hintBox, hideFunc, function() {
			pax.event.unbind( hintBox );
			pax.box.destroy( hintBox );
		} );
	};
					
	//	Internal function that shows the hint, message is an optional override of the message to show.
	var showHint = function( message, hintClass ) {
		//	Cancel any effects on the hint
		if( args.hideFunc || args.showFunc )pax.fx.cancelAll( hintId );
		window.clearTimeout( target.hideDelayTimeout );
	
		var dims = ( args.offsetElement )? pax.util.getPosition( args.offsetElement ): pax.util.getPosition( target );
		//	If there is a showFunc, we hide the box intially
		var displayStyle = ( args.showFunc )? 'none': 'block';
		hintBox = pax.box.show( hintId, ( message || args.message ), ( hintClass || args.hintClass ), dims.x + dims.width + args.x, dims.y + args.y, document.body, displayStyle );
		
		pax.css.opacity( hintBox, args.opacity );
		
		if( args.width != '' || args.height != '' ) {
			pax.util.setPosition( hintBox, {
				width: (args.width)? args.width: '',
				height: (args.height)? args.height: ''
			} );
		}
		
		target.hideHint = hideHint;
						
		//	Allow clicking anywhere on the hint, to make it disappear
		if( args.clickToHide ) {


						
			pax.event.bind( hintBox, 'click', function() { hideHint( args.hideFunc ) } );


/*
			pax.event.bind( hintBox, 'click', function() {
				pax.box.hide( hintBox, args.hideFunc, function() {
					pax.event.unbind( hintBox );
					pax.box.destroy( hintBox );
				} );
			} );
	*/		
			
			
			
		}
	
		if( args.dontHideOnHover ) {
			//	Bind hint mouse over function
			pax.event.bindOne( hintBox, 'mouseover', function() {
				//	Clear the hide event timer
				window.clearTimeout( target.hideDelayTimeout );
				//	Attach hide functions
				//	Unless we're only hiding on click
				if( !args.clickToHide ) {
					pax.event.bindEventList( hintBox, args.hideEvents, function() {
						window.clearTimeout( target.showDelayTimeout );
						window.clearTimeout( target.hideDelayTimeout );
						/*
						var hideFunc = function() {
							pax.box.hide( hintBox, args.hideFunc, function() {
								pax.event.unbind( hintBox );
								pax.box.destroy( hintBox );
							} );
						};
						*/
						//target.hideHint = hideFunc;
						
						target.hideDelayTimeout = window.setTimeout( function() { hideHint( args.hideFunc ) }, args.hideDelay );
					} );
				}
			} );
		}

		//	Apply the show func; we start the function, then set the display style to block
		if( args.showFunc ) {
			var id = (typeof(hintBox.id) != 'undefined' )? hintBox.id: hintBox;
			args.showFunc( id );
			hintBox.style.display = 'block';
		}
	};

    target.showHint = showHint;
	
    pax.event.bindEventList( target, args.events, function() {
		
		window.clearTimeout( target.hideDelayTimeout );
		
		if( args.showDelay ) {
			window.clearTimeout( target.showDelayTimeout );
			target.showDelayTimeout = window.setTimeout( function() { showHint(); }, args.showDelay );
		} else {
			showHint();
		}
	} );
	
	//	Don't add a hide events, if we are using a click function to hide the hint
	if( ! args.clickToHide ) {
		//	Bind element hide function
		pax.event.bindEventList( target, args.hideEvents, function() {
			window.clearTimeout( target.showDelayTimeout );
			window.clearTimeout( target.hideDelayTimeout );
			target.hideDelayTimeout = window.setTimeout( function() {
				if( args.hideFunc || args.showFunc )pax.fx.cancelAll( hintId );
				
				hideHint( args.hideFunc );
				/*
				pax.box.hide( hintBox, args.hideFunc, function() {
					pax.event.unbind( hintBox );
					pax.box.destroy( hintBox );
				} );
				*/
			}, args.hideDelay );
		} );
	}
};

/*	Private Method: pax.widget.hint.model
	Setup the default config values for this widget
*/
pax.widget.hint.model = function( args ) {
	
	return pax.defaultArgs( {
		target: null,				//	Element to show the hint near, you MUST supply this.
		message: null,		 		//	Hint message
		messageAttribute: null,		//	If we want to use a specific attribute for the message text, eg: alt or title
		hintClass: 'hintMessage',	//	The default base class name, for applying other than the default style
		width: '',					//	Width of the hint in pixels, use the hintClass to override width
		height: '',					//	Height of the hint - leave this empty, for height as big as the content
		x: 5,						//	X Offset in pixels for the hint
		y: 5,						//	Y Offset in pixels for the hint
        offsetElement: null,        //  Element to offset the hint from
		opacity: 100,				//	Opacity in percent
		events: ['mouseover'],		//	Which event(s) to show the hint on
		hideEvents: ['mouseout'],	//	Which event(s) to hide the hint on
		clickToHide: false,			//	Do we wait for mouse out of the element, or a click on the hint?
		dontHideOnHover: false,		//	Do we wait for mouse out of the hint?
		showDelay: null,			//	Delay in milliseconds to wait till we show the hint on hover
		hideDelay: 20,				//	Delay in milliseconds to wait till we hide the hint on, should always be at least 20
		id: '',						//	Optional ID of hint box (it will be auto generated otherwise)
		showFunc: null,				//	Optional function to show the hint with
		hideFunc: null				//	Optional function to hide the hint with
	}, args );
};
